package com.acooly.module.openapi.client.provider.yl.utils;

import com.acooly.core.utils.Dates;
import com.acooly.core.utils.Strings;
import com.acooly.module.openapi.client.provider.yl.message.YlBatchDeductRequest;
import com.acooly.module.openapi.client.provider.yl.message.YlDeductQueryRequest;
import com.acooly.module.openapi.client.provider.yl.message.YlRealDeductRequest;
import com.acooly.module.openapi.client.provider.yl.message.YlbillDownloadRequest;
import com.acooly.module.openapi.client.provider.yl.message.dto.YlBatchRetInfo;
import com.acooly.module.openapi.client.provider.yl.message.dto.YlDeductBodyInfo;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;

import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * @author fufeng 2017/12/7 17:21.
 */
public class XmlHelper {


    public static String formatXml(Document doc,String encoding) {
        try {
            OutputFormat format = OutputFormat.createPrettyPrint();
            format.setEncoding(encoding);
            format.setIndent(false);
            format.setNewlines(false);
            format.setNewLineAfterDeclaration(false);
            StringWriter out = new StringWriter();
            XMLWriter writer = new XMLWriter(out, format);
            writer.write(doc);
            writer.close();
            return out.toString();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }//method



    /**
     * -------------------------单笔代扣---------------------------------
     * 构建支付请求报文
     * @param data
     * @return
     */
    public static String buildPayReqMsg(YlRealDeductRequest data){
        Document doc = DocumentHelper.createDocument();
        Element gzEl = doc.addElement("GZELINK");

        Element infoEl = gzEl.addElement("INFO");
        infoEl.addElement("TRX_CODE").setText(StringHelper.nvl(data.getTrxCode()));
        infoEl.addElement("VERSION").setText(StringHelper.nvl(data.getVersion()));
        infoEl.addElement("DATA_TYPE").setText(StringHelper.nvl(data.getDataType()));
        infoEl.addElement("LEVEL").setText(StringHelper.nvl(data.getLevel()));
        infoEl.addElement("USER_NAME").setText(StringHelper.nvl(data.getUserName()));
        infoEl.addElement("USER_PASS").setText(StringHelper.nvl(data.getUserPass()));
        infoEl.addElement("REQ_SN").setText(StringHelper.nvl(data.getReqSn()));
        infoEl.addElement("SIGNED_MSG").setText("");

        Element bodyEl = gzEl.addElement("BODY");
        Element transSumEl =  bodyEl.addElement("TRANS_SUM");
        transSumEl.addElement("BUSINESS_CODE").setText(StringHelper.nvl(data.getBusinessCode()));
        transSumEl.addElement("MERCHANT_ID").setText(StringHelper.nvl(data.getMerchantId()));
        transSumEl.addElement("SUBMIT_TIME").setText(StringHelper.nvl(data.getSubmitTime()));
        transSumEl.addElement("TOTAL_ITEM").setText(StringHelper.nvl(data.getTotalItem()));
        transSumEl.addElement("TOTAL_SUM").setText(StringHelper.nvl(data.getTotalSum()));

        Element transDetailsEl =  bodyEl.addElement("TRANS_DETAILS");

        Element transDetailEl =  transDetailsEl.addElement("TRANS_DETAIL");
        transDetailEl.addElement("SN").setText(StringHelper.nvl(data.getSn()));
        transDetailEl.addElement("E_USER_CODE");
        transDetailEl.addElement("BANK_CODE").setText(StringHelper.nvl(data.getBankCode()));
        transDetailEl.addElement("ACCOUNT_TYPE").setText(StringHelper.nvl(data.getAccountType()));
        transDetailEl.addElement("ACCOUNT_NO").setText(StringHelper.nvl(data.getAccountNo()));
        transDetailEl.addElement("ACCOUNT_NAME").setText(StringHelper.nvl(data.getAccountName()));
        transDetailEl.addElement("PROVINCE").setText(StringHelper.nvl(data.getProvince()));
        transDetailEl.addElement("CITY").setText(StringHelper.nvl(data.getCity()));
        transDetailEl.addElement("BANK_NAME").setText(StringHelper.nvl(data.getBankName()));
        transDetailEl.addElement("ACCOUNT_PROP").setText(StringHelper.nvl(data.getAccountProp()));
        transDetailEl.addElement("AMOUNT").setText(StringHelper.nvl(data.getAmount()));
        transDetailEl.addElement("CURRENCY").setText(StringHelper.nvl(data.getCurrency()));
        transDetailEl.addElement("PROTOCOL").setText(StringHelper.nvl(data.getProtocol()));
        transDetailEl.addElement("PROTOCOL_USERID").setText(StringHelper.nvl(data.getProtocolUserid()));
        transDetailEl.addElement("ID_TYPE").setText(StringHelper.nvl(data.getIdType()));
        transDetailEl.addElement("ID").setText(StringHelper.nvl(data.getId()));
        transDetailEl.addElement("TEL").setText(StringHelper.nvl(data.getTel()));
        transDetailEl.addElement("RECKON_ACCOUNT").setText(StringHelper.nvl(data.getReckonAccount()));
        transDetailEl.addElement("CUST_USERID").setText(StringHelper.nvl(data.getCustUserid()));
        transDetailEl.addElement("REMARK").setText(StringHelper.nvl(data.getRemark()));

        String xml = formatXml(doc,"GBK");

        return xml;
    }


    /**
     * -------------------------单笔代扣---------------------------------
     * 解析返回报文中的状态位
     * @param ylRespmsg
     * @param messageMap
     * @throws Exception
     */
    public static void parseYlRealMsgToDto(String ylRespmsg, Map<String, Object> messageMap) throws Exception {

        Document retDoc = DocumentHelper.parseText(ylRespmsg);
        Element msgEl = retDoc.getRootElement();
        Element infoEl = msgEl.element("INFO");

        messageMap.put("retCode",infoEl.elementText("RET_CODE"));
        messageMap.put("errMsg",infoEl.elementText("ERR_MSG"));
        messageMap.put("bizOrderNo",infoEl.elementText("REQ_SN"));

        Element bodyEl = msgEl.element("BODY");
        Element retDetails = bodyEl.element("RET_DETAILS");
        if(null != retDetails){
            Element retDetail = retDetails.element("RET_DETAIL");
            messageMap.put("retDetailCode",retDetail.elementText("RET_CODE"));
            messageMap.put("errDetailMsg",retDetail.elementText("ERR_MSG"));
            messageMap.put("amount",retDetail.elementText("AMOUNT"));
        }
    }// method


    /**
     *
     * -------------------------批量代扣---------------------------------
     * 构建支付请求报文    批量扣款
     * @return
     */
    public static String buildPayReqMsg(YlBatchDeductRequest data){
        Document doc = DocumentHelper.createDocument();
        Element gzEl = doc.addElement("GZELINK");

        Element infoEl = gzEl.addElement("INFO");
        infoEl.addElement("TRX_CODE").setText(StringHelper.nvl(data.getTrxCode()));
        infoEl.addElement("VERSION").setText(StringHelper.nvl(data.getVersion()));
        infoEl.addElement("DATA_TYPE").setText(StringHelper.nvl(data.getDataType()));
        infoEl.addElement("LEVEL").setText(StringHelper.nvl(data.getLevel()));
        infoEl.addElement("USER_NAME").setText(StringHelper.nvl(data.getUserName()));
        infoEl.addElement("USER_PASS").setText(StringHelper.nvl(data.getUserPass()));
        infoEl.addElement("REQ_SN").setText(StringHelper.nvl(data.getReqSn()));
        infoEl.addElement("SIGNED_MSG").setText("");

        Element bodyEl = gzEl.addElement("BODY");
        Element transSumEl =  bodyEl.addElement("TRANS_SUM");
        transSumEl.addElement("BUSINESS_CODE").setText(StringHelper.nvl(data.getBusinessCode()));
        transSumEl.addElement("MERCHANT_ID").setText(StringHelper.nvl(data.getMerchantId()));
        transSumEl.addElement("EXPECT_SEND_TIME").setText(StringHelper.nvl(data.getExpectSendTime()));
        transSumEl.addElement("TOTAL_ITEM").setText(StringHelper.nvl(data.getTotalItem()));
        transSumEl.addElement("TOTAL_SUM").setText(StringHelper.nvl(data.getTotalSum()));

        Element transDetailsEl =  bodyEl.addElement("TRANS_DETAILS");
        List<YlDeductBodyInfo> bodyTransDetails = data.getYlDeductBodyInfos();
        if(bodyTransDetails!=null && bodyTransDetails.size()>0){
            for(YlDeductBodyInfo details : bodyTransDetails){
                Element transDetailEl =  transDetailsEl.addElement("TRANS_DETAIL");
                transDetailEl.addElement("SN").setText(StringHelper.nvl(details.getBizOrderNo()));
                transDetailEl.addElement("E_USER_CODE").setText("");
                transDetailEl.addElement("BANK_CODE").setText(StringHelper.nvl(details.getPayBankId()));
                transDetailEl.addElement("ACCOUNT_TYPE").setText(StringHelper.nvl(details.getPayBankCardType()));
                transDetailEl.addElement("ACCOUNT_PROP").setText(StringHelper.nvl(details.getPayAccountType()));
                transDetailEl.addElement("ACCOUNT_NO").setText(StringHelper.nvl(details.getPayBankCardNo()));
                transDetailEl.addElement("ACCOUNT_NAME").setText(StringHelper.nvl(details.getPayRealName()));
                transDetailEl.addElement("PROVINCE").setText(StringHelper.nvl(details.getPayBankProvince()));
                transDetailEl.addElement("CITY").setText(StringHelper.nvl(details.getPayBankCity()));
                transDetailEl.addElement("BANK_NAME").setText(StringHelper.nvl(details.getPayBankName()));
                transDetailEl.addElement("AMOUNT").setText(StringHelper.nvl(details.getAmount().getCent()+""));
                transDetailEl.addElement("CURRENCY").setText(StringHelper.nvl(details.getCurrency()));
                transDetailEl.addElement("PROTOCOL").setText(StringHelper.nvl(details.getProtocolNo()));
                transDetailEl.addElement("PROTOCOL_USERID").setText("");
                transDetailEl.addElement("ID_TYPE").setText(StringHelper.nvl(details.getPayCertType()));
                transDetailEl.addElement("ID").setText(StringHelper.nvl(details.getPayCertNo()));
                transDetailEl.addElement("TEL").setText(StringHelper.nvl(details.getPayMobileNo()));
                transDetailEl.addElement("RECKON_ACCOUNT").setText("");
                transDetailEl.addElement("CUST_USERID").setText("");
                transDetailEl.addElement("REMARK").setText(StringHelper.nvl(details.getMemo()));
                transDetailEl.addElement("ELE_BANKNO").setText("");
                transDetailEl.addElement("ABS").setText("");
                transDetailEl.addElement("PS").setText("");
                transDetailEl.addElement("USE").setText("");
                transDetailEl.addElement("CRE_VAL_DATE").setText("");
                transDetailEl.addElement("CRE_CVN2").setText("");
                transDetailEl.addElement("ACC_PASS").setText("");
            }
        }
        String xml = formatXml(doc,"GBK");
        return xml;
    }







    /**
     * -------------------------批量代扣---------------------------------
     * 解析返回报文中的状态位
     * @param ylRespmsg
     * @param messageMap
     * @throws Exception
     */
    public static void parseYlBatchMsgToDto(String ylRespmsg, Map<String, Object> messageMap) throws Exception {
        Document retDoc = DocumentHelper.parseText(ylRespmsg);

        //得到根节点
        Element msgEl = retDoc.getRootElement();

        //得到根节点下的info节点
        Element infoEl = msgEl.element("INFO");
        messageMap.put("batchNo",infoEl.elementText("REQ_SN"));
        messageMap.put("retCode",infoEl.elementText("RET_CODE"));
        messageMap.put("errMsg",infoEl.elementText("ERR_MSG"));
        //得到根节点下的body节点
        Element bodyEl = msgEl.element("BODY");
        //得到body节点下的TRANS_DETAILS节点
        Element detailsEl = bodyEl.element("RET_DETAILS");
        List<YlBatchRetInfo> btds = new ArrayList<YlBatchRetInfo>();
        if(null != detailsEl){
            //得到TRANS_DETAILS节点下的TRANS_DETAIL节点
            Iterator detailIter = detailsEl.elementIterator("RET_DETAIL");
            // 遍历body节点
            while (detailIter.hasNext()) {
                Element detailEle = (Element) detailIter.next();
                YlBatchRetInfo detail = new YlBatchRetInfo();
                detail.setBizOrderNo(detailEle.elementTextTrim("SN"));
                detail.setRetCode(detailEle.elementTextTrim("RET_CODE"));
                detail.setErrMsg(detailEle.elementTextTrim("ERR_MSG"));
                btds.add(detail);
            }
        }
        messageMap.put("batchRetDetils",btds);
    }// method



    /**
     * -------------------------查询代扣结果--------------------------------
     * 构建支付请求报文
     * @param data
     * @return
     */
    public static String buildPayReqMsg(YlDeductQueryRequest data){
        Document doc = DocumentHelper.createDocument();
        Element gzEl = doc.addElement("GZELINK");

        Element infoEl = gzEl.addElement("INFO");
        infoEl.addElement("TRX_CODE").setText(StringHelper.nvl(data.getTrxCode()));
        infoEl.addElement("VERSION").setText(StringHelper.nvl(data.getVersion()));
        infoEl.addElement("DATA_TYPE").setText(StringHelper.nvl(data.getDataType()));
        infoEl.addElement("REQ_SN").setText(StringHelper.nvl(data.getReqSn()));//交易流水号
        infoEl.addElement("USER_NAME").setText(StringHelper.nvl(data.getUserName()));
        infoEl.addElement("USER_PASS").setText(StringHelper.nvl(data.getUserPass()));
        infoEl.addElement("SIGNED_MSG").setText("");

        Element bodyEl = gzEl.addElement("BODY");
        Element transSumEl =  bodyEl.addElement("QUERY_TRANS");
        transSumEl.addElement("QUERY_SN").setText(StringHelper.nvl(data.getQuerySn()));
        transSumEl.addElement("QUERY_REMARK").setText(StringHelper.nvl(data.getQueryRemark()));
        transSumEl.addElement("RET_TYPE").setText(StringHelper.nvl(data.getRetType()));

        if(Strings.isNotEmpty(data.getQueryDetailSn())){
            Element details =  bodyEl.addElement("QUERY_DETAILS");
            Element detail =  details.addElement("QUERY_DETAIL");
            detail.addElement("QUERY_DETAIL_SN").setText(StringHelper.nvl(data.getQueryDetailSn()));
        }

        String xml = formatXml(doc,"GBK");
        return xml;
    }



    /**
     * -------------------------代扣结果--------------------------------
     * 解析返回报文中的状态位
     * @param ylRespmsg
     * @param messageMap
     * @throws Exception
     */
    public static void parseYlQueryMsgToDto(String ylRespmsg, Map<String, Object> messageMap) throws Exception {


        Document retDoc = DocumentHelper.parseText(ylRespmsg);
        Element msgEl = retDoc.getRootElement();
        Element infoEl = msgEl.element("INFO");

        messageMap.put("retCode",infoEl.elementText("RET_CODE"));
        messageMap.put("errMsg",infoEl.elementText("RET_CODE"));

        Element bodyEl = msgEl.element("BODY");
        Element queryTrans = bodyEl.element("QUERY_TRANS");
        messageMap.put("bizOrderNo",queryTrans.elementText("QUERY_SN"));
        Element retDetails = bodyEl.element("RET_DETAILS");
        if(null != retDetails){
            Iterator ite = retDetails.nodeIterator();
            while(ite.hasNext()){
                Element retDetail = (Element) ite.next();
                messageMap.put("payBankCardNo",retDetail.elementText("ACCOUNT"));
                messageMap.put("payRealName",retDetail.elementText("ACCOUNT_NAME"));
                messageMap.put("amount",retDetail.elementText("AMOUNT"));
                messageMap.put("retDetailCode",retDetail.elementText("RET_CODE"));
                messageMap.put("errDetailMsg",retDetail.elementText("ERR_MSG"));
            }
        }
    }// method

    /**
     * -------------------------对帐文件下载---------------------------------
     * 构建支付请求报文
     * @param partnerInfo
     * @return
     */
    public static String buildPayReqMsg(YlbillDownloadRequest partnerInfo){
        String filePath = partnerInfo.getFilePath();
        partnerInfo.setFilePath(filePath += "_" + partnerInfo.getPartnerId() + ".txt");
        // 准备数据
        String now = Dates.format(new Date(), "yyyyMMddHHmmss");
        // 待签名数据
        String beSignStr = partnerInfo.getPeriodNo() + "|" + partnerInfo.getSettNo() + "|" + partnerInfo.getPartnerName() + "|"
                + partnerInfo.getPartnerId() + "|" + now;
        return beSignStr;
    }




    @SuppressWarnings("unchecked")
    public static Map<String, Object> Dom2Map(Document doc){
        Map<String, Object> map = new HashMap<String, Object>();
        if(doc == null)
            return map;
        Element root = doc.getRootElement();
        for (Iterator iterator = root.elementIterator(); iterator.hasNext();) {
            Element e = (Element) iterator.next();
            List list = e.elements();
            if(list.size() > 0){
                map.put(e.getName(), Dom2Map(e));
            }else
                map.put(e.getName(), e.getText());
        }
        return map;
    }
    @SuppressWarnings("unchecked")
    public static Map Dom2Map(Element e){
        Map map = new HashMap();
        List list = e.elements();
        if(list.size() > 0){
            for (int i = 0;i < list.size(); i++) {
                Element iter = (Element) list.get(i);
                List mapList = new ArrayList();

                if(iter.elements().size() > 0){
                    Map m = Dom2Map(iter);
                    if(map.get(iter.getName()) != null){
                        Object obj = map.get(iter.getName());
                        if(!obj.getClass().getName().equals("java.util.ArrayList")){
                            mapList = new ArrayList();
                            mapList.add(obj);
                            mapList.add(m);
                        }
                        if(obj.getClass().getName().equals("java.util.ArrayList")){
                            mapList = (List) obj;
                            mapList.add(m);
                        }
                        map.put(iter.getName(), mapList);
                    }else
                        map.put(iter.getName(), m);
                }
                else{
                    if(map.get(iter.getName()) != null){
                        Object obj = map.get(iter.getName());
                        if(!obj.getClass().getName().equals("java.util.ArrayList")){
                            mapList = new ArrayList();
                            mapList.add(obj);
                            mapList.add(iter.getText());
                        }
                        if(obj.getClass().getName().equals("java.util.ArrayList")){
                            mapList = (List) obj;
                            mapList.add(iter.getText());
                        }
                        map.put(iter.getName(), mapList);
                    }else
                        map.put(iter.getName(), iter.getText());
                }
            }
        }else
            map.put(e.getName(), e.getText());
        return map;
    }




    public static void main(String[] args) {
            String xmlStr = "<?xml version=\"1.0\" encoding=\"GBK\"?><GZELINK><INFO><TRX_CODE>100004</TRX_CODE><VERSION>05</VERSION><DATA_TYPE>2</DATA_TYPE><REQ_SN>o17122210162962270003b</REQ_SN><RET_CODE>0000</RET_CODE><ERR_MSG>处理完成</ERR_MSG><SIGNED_MSG>2796e0845bb1d46b613f54d355d72e3d45af1b42fb4f5f541ea9d90436466db57243cd1c0aad7e2c90436570675511debb1e84c3ab4e393570e5584d57169a39df26614c5af59d5c0627e90f5d496f0a10525e4d5d84ad4aee7ad7ea10a14078b088364448fe59558b20f9de0e8198282ffc227361675088574c41bc20dd69aa</SIGNED_MSG></INFO><BODY><RET_DETAILS><RET_DETAIL><SN>o17122210162962270003b</SN><ACCOUNT_NO>62220238012332123450</ACCOUNT_NO><ACCOUNT_NAME>傅枫15</ACCOUNT_NAME><AMOUNT>40000</AMOUNT><CUST_USERID></CUST_USERID><REMARK></REMARK><RET_CODE>0000</RET_CODE><ERR_MSG>交易?成功</ERR_MSG></RET_DETAIL></RET_DETAILS></BODY></GZELINK>";

        try {
            Document doc = DocumentHelper.parseText(xmlStr);

            System.out.println(doc.asXML());

            long beginTime = System.currentTimeMillis();

            Map<String, Object> map = Dom2Map(doc);

            System.out.println(map.toString());

            System.out.println("Use time:"+(System.currentTimeMillis()-beginTime));
        } catch (DocumentException e){
            e.printStackTrace();
        }

    }

}


